<?php
/**
 * Created by Administrator
 * User: longli
 * VX: isa1589518286
 * Date: 2020/08/19
 * Time: 10:28
 * @link http://www.lmterp.cn
 */

namespace app\common\service\import;

use app\common\library\FileExcel;
use app\common\library\Tools;
use app\common\service\BaseService;
use Env;
use RuntimeException;

/**
 * 数据导入基类
 * Class BaseImport
 * @package app\common\service\import
 */
abstract class BaseImport extends BaseService
{
    /**
     * excel 或者 csv 文件
     * @var string
     */
    protected $file;

    /**
     * 跳过的行数
     * @var int
     */
    protected $line = 0;

    /**
     * 错误信息
     * @var array
     */
    protected $error = [];

    /**
     * 不导入字段
     * @var array
     */
    protected $excludeField = [];

    /**
     * 要处理的数据
     * @var array
     */
    protected $data;

    public function __construct($file)
    {
        $this->file = $file;
        if(!$this->checkFile())
        {
            throw new RuntimeException(join(", ", $this->getError()));
        }
        $this->init();
    }

    /**
     * 初始化方法
     * @date 2020/08/21
     * @author longli
     */
    public function init()
    {}

    /**
     * 执行导入
     * @date 2020/08/21
     * @author longli
     * @return bool
     * @throws RuntimeException
     */
    public function run()
    {
        if($this->readFile()) return $this->write();
        throw new RuntimeException(join("\n", $this->getError()));
    }

    /**
     * 读取文件
     * @date 2020/08/21
     * @author longli
     * @return bool
     */
    public function readFile()
    {
        $excel = new FileExcel($this->file);
        $excel->setLine($this->line);
        $result =$excel->read($this->getHeader(), function($item, $key, &$error)
        {
            $line = $key + 2;
            if(($msg = $this->validate($item, $key)) !== true)
            {
                $error[] = "第【{$line}】行：$msg";
                return null;
            }
            return $item;
        });
        if(count($excel->getError()) > 0)
        {
            $this->setError($excel->getError());
            return false;
        }
        $this->data = $result;
        return true;
    }

    /**
     * 写入数据
     * @date 2020/08/21
     * @author longli
     * @return bool
     */
    abstract protected function write();

    /**
     * 验证导入的信息是否正确
     * @param array $row 读进来的一行数据
     * @param int 当前验证下标
     * @date 2020/08/21
     * @author longli
     * @return bool|string
     */
    abstract protected function validate(& $row = [], $key = 0);

    /**
     * 转换为可写入数据库的格式
     * @param array $row 一行数据
     * @return void
     * @date 2020/09/16
     * @author longli
     */
    abstract protected function transform(& $row = []);

    /**
     * 自定义读取头信息
     * @return array
     * @date 2020/08/29
     * @author longli
     */
    protected function getHeader()
    {
        return [];
    }

    /**
     * 检查文件格式是否合法
     * @date 2020/08/21
     * @author longli
     * @return bool
     */
    public function checkFile()
    {
        if(!is_file($this->file))
        {
            $this->error[] = "文件不存在";
        }
        else if(!is_readable($this->file))
        {
            $this->error[] = "文件不可读";
        }

        $allow = ["csv", "xlsx", "xls"];
        $suffix = strtolower(Tools::getFileSuffix($this->file));
        if(!in_array($suffix, $allow))
        {
            $this->error[] = "文件格式有误, 只允许导入以下格式: " . join(', ', $allow);
        }
        return empty($this->error);
    }

    /**
     * 验证必填字段
     * @param array $data 数据
     * @param array $validate 需要被验证
     * @param array $translate 字段错误提示信息
     * @date 2020/08/29
     * @return bool|array
     *@author longli
     */
    protected function requireField($data = [], $validate = [], $translate = [])
    {
        $error = [];
        foreach($validate as $r)
        {
            if(empty($data[$r]) || (isset($data[$r]) && $data[$r] === ''))
                $error[] =  "【{$translate[$r]}】不能为空";
        }
        return !empty($error) ? $error : true;
    }

    /**
     * 验证必填一个字段
     * @param array $data 数据
     * @param array $validate 需要被验证
     * @param array $translate 字段错误提示信息
     * @date 2020/08/29
     * @return bool|array
     *@author longli
     */
    protected function requireOr($data = [], $validate = [], $translate = [])
    {
        $error = [];
        $flag = true;
        foreach($validate as $r)
        {
            if(!empty($data[$r]) || (isset($data[$r]) && is_numeric($data[$r])))
            {
                $flag = false;
                break;
            }
        }
        if($flag)
        {
            foreach($validate as $r)
            {
                $error[] = $translate[$r];
            }
        }
        return !empty($error) ? $error : true;
    }

    /**
     * 验证是否为图片
     * @param array $data 数据
     * @param array $validate 需要被验证
     * @param array $translate 字段错误提示信息
     * @return bool|array
     * @date 2020/09/11
     * @author longli
     */
    protected function validateImage($data = [], $validate = [], $translate = [])
    {
        $error = [];
        foreach($validate as $r)
        {
            if(empty($data[$r])) continue;
            if(!Tools::startWith($data[$r], 'http', false)
                && !is_file(Env::get("root_path") . "public{$data[$r]}"))
                $error[] = $translate[$r] . "【{$data[$r]}】不是图片";
        }
        return !empty($error) ? $error : true;
    }

    /**
     * 验证是否为 url
     * @param array $data 数据
     * @param array $validate 需要被验证
     * @param array $translate 字段错误提示信息
     * @return bool|array
     * @date 2020/09/11
     * @author longli
     */
    protected function validateUrl($data = [], $validate = [], $translate = [])
    {
        $error = [];
        foreach($validate as $r)
        {
            if(empty($data[$r])) continue;
            if(!Tools::startWith($data[$r], 'http', false))
                $error[] = $translate[$r] . "【{$data[$r]}】不是正确的链接";
        }
        return !empty($error) ? $error : true;
    }

    /**
     * 验证填入后的值是否为数字
     * @param array $data 数据
     * @param array $validate 需要被验证
     * @param array $translate 字段错误提示信息
     * @date 2020/08/29
     * @return bool|array
     *@author longli
     */
    protected function validateNumber($data = [], $validate = [], $translate = [])
    {
        $error = [];
        foreach($validate as $r)
        {
            if(!empty($data[$r]) && !is_numeric($data[$r]))
                $error[] = $translate[$r] . "【{$data[$r]}】不是数字";
        }
        return !empty($error) ? $error : true;
    }

    /**
     * 验证填入后的值是否为时间
     * @param array $data 数据
     * @param array $validate 需要被验证
     * @param array $translate 字段错误提示信息
     * @date 2020/08/29
     * @return bool|array
     *@author longli
     */
    protected function validateDate($data = [], $validate = [], $translate = [])
    {
        $error = [];
        foreach($validate as $r)
        {
            if(!empty($data[$r]) && strtotime($data[$r]) === false)
                $error[] = $translate[$r] . "【{$data[$r]}】时间格式有误";
        }
        return !empty($error) ? $error : true;
    }

    /**
     * @return array
     */
    public function getError()
    {
        return $this->error;
    }

    /**
     * @param array $error
     */
    public function setError(array $error)
    {
        $this->error = $error;
    }

    /**
     * @return array
     */
    public function getData()
    {
        return $this->data;
    }
}